オプション解析モジュール plac を使ってみよう
オプション解析モジュール plac を使ってみましょう。
インストール
拡張モジュールなので次のようにインストールします。
code: bash condaの場合
$ conda install plac
code: bash pipの場合
$ pip install plac
簡単な使用例
次のコードはコマンドライン引数を sys.args で実装したものです。
code: plac_sysargs.py
from datetime import datetime
def main(dsn, table='product', today=datetime.today()):
"Do something with the database"
print("ok")
if __name__ == '__main__':
import sys
if n == 0:
sys.exit('usage: python %s dsn' % sys.argv0) elif n == 1:
else:
sys.exit('Unrecognized arguments: %s' % ' '.join(sys.argv2:)) これを plac を使うとほとんどコードを修正することなく、オプション解析処理を実装することができます。
code: plac_call_demo.py
from datetime import datetime
def main(dsn, table='product', today=datetime.today()):
"Do something on the database"
print(dsn, table, today)
if __name__ == '__main__':
import plac
plac.call(main)
plac.call()で対象の関数を呼び出すだけです。
code: bash
$ python plac_call_demo.py --help
Do something on the database
positional arguments:
dsn
optional arguments:
-h, --help show this help message and exit
$ python plac_call_demo.py
plac_call_demo.py: error: the following arguments are required: dsn
code: plac_call_demo2.py
from datetime import datetime
def main(dsn, *scripts):
"Run the given scripts on the database"
for script in scripts:
print('executing %s' % script)
# ...
if __name__ == '__main__':
import plac
plac.call(main)
code: bash
$ python plac_call_demo2.py --help
Run the given scripts on the database
positional arguments:
dsn
scripts
optional arguments:
-h, --help show this help message and exit
plac はcall() に与えた関数の引数を調べてオプション解析処理を行います。
この例のように、モジュールとしてインポートされたときには何も影響を与えずに、スクリプトコマンドとして呼び出されたときだけ placモジュールをロードしてオプション解析処理を追加することができます。
ヘルプメッセージを変えたい
Python3で導入された機能のひとつに関数アノテーションがあります。plac はこれを調べているため、ここの文字列がヘルプメッセージに利用されます。
code: plac_call_demo3.py
from datetime import datetime
def main(dsn: "Database dsn", *scripts: "SQL scripts"):
"Run the given scripts on the database"
for script in scripts:
print('executing %s' % script)
# ...
if __name__ == '__main__':
import plac
plac.call(main)
code: bash
$ python plac_call_demo3.py --help
Run the given scripts on the database
positional arguments:
dsn Database dsn
scripts SQL scripts
optional arguments:
-h, --help show this help message and exit
オプション処理
placでオプション処理をさせるためにも、アノテーションを利用します。
code: plac_call_option.py
def main(command: ("SQL query", 'option', 'c'), dsn):
if command:
print('executing %s on %s' % (command, dsn))
# ...
if __name__ == '__main__':
import plac
plac.call(main)
タプルをアノテーションとして与えています。
タプルの第1要素がヘルプメッセージ、第2要素がplacにオプションとして処理することを伝えるための指示、第3要素がショートオプションとして認識させる文字となります。
ショートオプションは省略することができます。
code: bash
$ python plac_call_option.py --help
positional arguments:
dsn
optional arguments:
-h, --help show this help message and exit
-c COMMAND, --command COMMAND
SQL query
$ python plac_call_option.py -c "select * from table" dsn
executing select * from table on dsn
$ python plac_call_option.py --command="select * from table" dsn
executing select * from table on dsn
placではオプションを曖昧に指定しても、該当するオプションが指定されたものとして処理してくれます。
code: bash
$ python plac_call_option.py --comm="select * from table" dsn
executing select * from table on dsn
フラグオプション処理
オプションをフラグとしたいときは、オプション処理のときと同じようにアノテーションで記述します。このときタプルの第2要素を flag とします。
code: plac_call_flag.py
def main(verbose: ('prints more info', 'flag', 'v'), dsn: 'connection string'):
if verbose:
print('connecting to %s' % dsn)
# ...
if __name__ == '__main__':
import plac
plac.call(main)
オプションのアンダースコア
Pythonでは変数名にマイナス記号(-)は使えないため、次の例にある dry_run のように、アンダースコア(_)がよく使われます。
code: plac_call_underscore.py
def main(dry_run: ('Dry run', 'flag', 'd')):
if dry_run:
print('Doing nothing')
else:
print('Doing something')
if __name__ == '__main__':
import plac
plac.call(main)
plac は 変数 dry_run からオプション文字として dry-run を作りだします。
code: bash
$ python plac_call_flag2.py --help
usage: plac_call_flag2.py -h -d optional arguments:
-h, --help show this help message and exit
-d, --dry-run Dry run
$ python plac_call_flag2.py -d
Doing nothing
$ python plac_call_flag2.py --dry-run
Doing nothing
$ python plac_call_flag2.py --dry_run
usage: plac_call_flag2.py -h -d plac_call_flag2.py: error: unrecognized arguments: --dry_run
アノテーションでのタプル
アノテーションとして与えるタプルは次のような指定ができます。
(help, kind, abbrev, type, choices, metavar)
help: ヘルプ文字列
kind: オプションのタイプ、"flag"、 "option"、 "positional"
abbrev: ショートオプションでの文字
type: 指定したPythonの型に自動的に変換する。デフォルトはNoneで無変換
choices: 選択肢、デフォルトはNone
metavar: ヘルプメッセージで使用されるオプションや引数の文字列
位置引数の場合は、引数名を変更するために使用されます(そこにのみ)
デフォルトはNoneで、変数名を大文字にしたものが使われます
@plac.annotations()
関数アノテーションでplacに指示していくと、オプションや引数が多くなるに従って対象の関数の可読性が悪くなっていきます。
何より、できるだけ元のコードに手を加えたくないものです。
こうしたときは、@plac.annotations()デコレータを使います。
code: plac_annotation.py
import plac
@plac.annotations(
operator=("The name of an operator",
numbers=("A number",
'positional', None, float, None, "n"))
def main(operator, *numbers):
"A script to add and multiply numbers"
if operator == 'mul':
op = float.__mul__
result = 1.0
else: # operator == 'add'
op = float.__add__
result = 0.0
for n in numbers:
result = op(result, n)
return result
if __name__ == '__main__':
print(plac.call(main))
code: bash
$ python plac_annotation1.py --hep
usage: plac_annotation1.py -h {add,mul} [n n ...] plac_annotation1.py: error: the following arguments are required: operator
$ python plac_annotation1.py add 1 2
3.0
$ python plac_annotation1.py mul 2 3
6.0
自動処理
plac.call() はユーザが与えるコマンドライン引数やオプションをリストとして受け取ることができます。
これを利用すると次のような自動処理でテストを行うことが簡単にできます。
code: plac_auto.py
import plac
import plac_annotation
tests = [
]
for testcase in tests:
x = plac.call(plac_annotation.main, testcase)
print(f'testcase: {testcase}, result:{x}')
code:bash
$ python plac_autotest.py
ジェネレータ関数
plac.call() はジェネレータ関数もサポートしています。
code: plac_generator.py
import plac
def main(n):
for i in range(int(n)):
yield i
result = plac.call(main, '3') print(result)
plac.call() はジェネレータ関数の出力をリストにして返します。
code: bash
$ python plac_generator.py
可変長引数
可変長引数を受け取る関数に対して plac を適用し たいときがあります。
こうしたときはplac.call() で呼び出すのではなく、@plac.annotations()デコレータを次のように定義します。
code: plac_kwargs.py
import plac
@plac.annotations(
opt=('some option', 'option'),
args='default arguments',
kw='keyword arguments')
def main(opt, *args, **kw):
if opt:
yield 'opt=%s' % opt
if args:
yield 'args=%s' % str(args)
if kw:
yield 'kw=%s' % kw
if __name__ == '__main__':
for output in plac.call(main):
print(output)
code: bash
$ python plac_kwargs.py --help
positional arguments:
args default arguments
kw keyword arguments
optional arguments:
-h, --help show this help message and exit
-opt OPT some option
$ python plac_kwargs.py 1 2
args=('1', '2')
$ python plac_kwargs.py 1 2 firstname=Freddie lastname=Mercury
args=('1', '2')
kw={'firstname': 'Freddie', 'lastname': 'Mercury'}
plac には他にもインタプリタやサブコマンドを実装することができたり、並列処理などの機能もあるのですが、この資料ではここまでとしておきます。
参考: